﻿/*
COPYRIGHT (C) 2011-2013 Raynald Messié

THIS FILE IS PART OF SCRUMPILOT.

SCRUMPILOT IS FREE SOFTWARE: YOU CAN REDISTRIBUTE IT AND/OR MODIFY IT UNDER THE TERMS OF 
THE GNU LESSER GENERAL PUBLIC LICENSE VERSION v2.1 AS PUBLISHED BY THE FREE SOFTWARE FOUNDATION.

SCRUMPILOT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY; WITHOUT
EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU LESSER
GENERAL PUBLIC LICENSE FOR MORE DETAILS.

YOU SHOULD HAVE RECEIVED A COPY OF THE GNU LESSER GENERAL PUBLIC LICENSE ALONG WITH SCRUMPILOT. 
IF NOT, SEE <HTTP://WWW.GNU.ORG/LICENSES/>.
*/

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using ScrumPilot.Domain.Models;
using ScrumPilot.TFSHelpers;

namespace ScrumPilot.Domain.BPL
{
    public class TaskServices : BusinessProcessLayer
    {
        public TaskServices(TFSServices tfs)
            : base(tfs)
        {
        }

        public List<TaskForList> GetTasks(int userId, CultureInfo ci)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                this.CurrentUserID = userId;
                var selectedProjects = this.CurrentUser.FilterListByProject;
                var lstRet = new List<TaskForList>();

                if (selectedProjects != null)
                {
                    var projects = unitOfWork.ProjectRepository.GetMany(p => p.notuse == false && selectedProjects.Contains(p.projectid));
                    // apply filter
                    var filterType = GetFilterWorkItemTypeString();
                    var filterState = GetFilterTasksList();
                    if (filterType.Count > 0 && filterState.Count > 0)
                    {
                        var bFilterUnestimed = CurrentUser.FilterListUnestimated;
                        var qry = new TFSQuery(TFS.TfsWorkItemStore);
                        var wil = qry.GetWorkItemList(projects.Select(p => p.name).ToList(), filterState, filterType,
                                                      true);
                        if (wil != null)
                        {
                            if (bFilterUnestimed)
                                wil = wil.Where(p => p.GetScoringDate() == null).ToList();
                            // Todo : Parallelize foreach
                            wil.ForEach(wie => lstRet.Add(TransformWIEinTaskForList(wie)));
                        }
                    }

                    return lstRet.OrderByDescending(p => p.Date).ToList();
                }
                return lstRet;
            }
        }

        public Task GetTask(Int32 id, CultureInfo ci)
        {
            var t = id > 0 ? TransformWIEinTask(TFS.GetWorkItem(id, false), TFS) : new Task();
            var pm = new ProjectServices(TFS);
            t.Sprints = new Dictionary<string, string>(pm.GetSprints().ToDictionary(p => p.ID, p => p.Name));
            t.Areas = new Dictionary<string, string>(pm.GetAreas(t.Sprint).ToDictionary(p => p.ID, p => p.Name));
            t.State = t.State < 0 ? WorkItemState.Todo : t.State;
            var tskForScore = this.GetTaskScore(id);

            var wie = TFS.GetWorkItem(id, false);

            t.Creator = wie.CreatedBy;
            t.CreateDate = wie.CreatedDate;
            List<TFSAttachment> tAttachments = wie.ReadAttachments(TFS);
            t.Attachments = new List<TFSAttachment>();
            foreach (var attachment in tAttachments)
            {
                t.Attachments.Add(attachment);
            }

            if (wie.GetScoringDate() == null)
            {
                t.Score = 0;
                return t;
            }
            t.Score = tskForScore.Level * tskForScore.Priority * tskForScore.Duration;
            return t;
        }

        private TaskForList TransformWIEinTaskForList(Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem wie)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                var dbProject = unitOfWork.ProjectRepository.GetById(wie.Project.Id);
                if (dbProject != null)
                {
                    String projectName = dbProject.name;
                    String sprint = dbProject.currentsprint;
                    var state = wie.GetState();
                    var tsk = new TaskForList
                                  {
                                      ID = wie.Id,
                                      Date = wie.ChangedDate,
                                      Name = wie.Title,
                                      ProjectName = projectName,
                                      ProjectID = wie.Project.Id,
                                      Sprint = CutProjectName(projectName, sprint),
                                      Area = CutProjectName(projectName, wie.AreaPath),
                                      Description = wie.Description ?? "",
                                      State = state.ToString(),
                                      Score = wie.GetScrumPilotScore(),
                                      AssignedTo = ""
                                  };
                    switch (state)
                    {
                        case WorkItemState.Reopen:
                            tsk.IsReopen = true;
                            tsk.ActionToDo = true;
                            break;
                        case WorkItemState.Todo:
                            tsk.ActionToDo = true;
                            break;
                        case WorkItemState.InProgress:
                            tsk.ActionInProgress = true;
                            break;
                        case WorkItemState.ToTest:
                            tsk.ActionToTest = true;
                            break;
                        case WorkItemState.Done:
                            tsk.ActionDone = true;
                            break;
                    }
                    tsk.IsBug = wie.GetWorkItemType() == WorkItemType.Defect;
                    if (tsk.IsBug)
                    {
                        tsk.Description = wie.GetOtherDescription();
                    }
                    string assignee = wie.GetAssignedTo();
                    if (!String.IsNullOrEmpty(assignee))
                    {
                        var usr = unitOfWork.UserRepositery.Get(p => p.name == assignee);
                        Helpers.Helpers.SetAssignedTo(tsk, usr);
                        if (tsk.AssignedToID != null)
                            tsk.AssignedToMe = tsk.AssignedToID.Value == this.CurrentUserID;
                    }
                    return tsk;
                }
                return null;
            }
        }

        public Task TransformWIEinTask(Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem wie, TFSServices tfs)
        {
            var tsk = new Task
            {
                ID = wie.Id,
                Title = wie.Title,
                Sprint = wie.IterationPath,
                Area = wie.AreaId.ToString(CultureInfo.InvariantCulture),
                Description = wie.Description ?? "",
                State = wie.GetState(),
                AssignedTo = wie.GetAssignedTo()
            };
            string assignee = wie.GetAssignedTo();
            if (!String.IsNullOrEmpty(assignee))
            {
                using (var unitOfWork = new UnitOfWork())
                {
                    var usr = unitOfWork.UserRepositery.Get(p => p.name == assignee);
                    Helpers.Helpers.SetAssignedTo(tsk, usr);
                }
            }
            //   SPHelpers.SetAssignedTo(tsk, tfs.CheckIdentity(tfs.GetTFSIndentityByName(wie.GetAssignedTo()), false));
            return tsk;
        }

        public Boolean UpdateInlineWorkItem(String fieldType, Int32 id, String value)
        {
            var wie = TFS.GetWorkItem(id, true);
            if (fieldType == "title")
                wie.Title = value;
            else if (fieldType == "description")
                if (wie.GetWorkItemType() == WorkItemType.Defect)
                    wie.SetOtherDescription(value);
                else
                    wie.Description = value;

            return TFS.SaveWorkItem(wie);
        }

        public Boolean UpdateTask(Task myTask)
        {
            Boolean bRet = false;
            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem wie = null;
            if (myTask.ID > 0) // Update 
                wie = TFS.GetWorkItem(myTask.ID, true); // retrieve and lock WI

            if (wie != null)
            {
                wie.Description = myTask.Description;
                if (myTask.Area != "-1")
                    wie.AreaId = int.Parse(myTask.Area);
                if (wie.GetState() != myTask.State)
                    wie.SetState(myTask.State);
                //wie.AssignedTo = myTask.AssignedTo;
                bRet = TFS.SaveWorkItem(wie);
            }
            return bRet;
        }

        public Boolean UpdateStateTask(int taskID, WorkItemState newState)
        {
            Boolean bRet = false;
            var wie = TFS.GetWorkItem(taskID, true); // retrieve and lock WI
            if (wie != null)
            {
                if (wie.GetState() != newState)
                {
                    wie.SetState(newState);
                    bRet = TFS.SaveWorkItem(wie);
                }
            }
            return bRet;
        }

        public Boolean UpdateAssignedTask(int taskID, string userID)
        {
            Boolean bRet = false;
            var wie = TFS.GetWorkItem(taskID, true); // retrieve and lock WI
            if (wie != null)
            {
                if (wie.GetAssignedTo() != userID)
                {
                    wie.SetAssignedTo(userID);
                    bRet = TFS.SaveWorkItem(wie);
                }
            }
            return bRet;
        }

        public void SetFilterTasksList(int userId, int[] data)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                var usr = unitOfWork.UserRepositery.GetById(userId);
                usr.FilterListByProject = data;
                unitOfWork.UserRepositery.Update(usr);
                unitOfWork.Save();
            }
        }

        public void SetFilterTasksList(int userId, string key, Boolean data)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                var usr = unitOfWork.UserRepositery.GetFullById(userId);
                this.CurrentUserID = userId;
                switch (key)
                {
                    case "todochk":
                        usr.FilterListToDo = data;
                        break;
                    case "progresschk":
                        usr.FilterListInProgress = data;
                        break;
                    case "testchk":
                        usr.FilterListToTest = data;
                        break;
                    case "donechk":
                        usr.FilterListDone = data;
                        break;
                    case "reopenchk":
                        usr.FilterListReopen = data;
                        break;
                    case "unestimatedchk":
                        usr.FilterListUnestimated = data;
                        break;
                    case "taskflt":
                        usr.FilterListTask = data;
                        break;
                    case "bugflt":
                        usr.FilterListBug = data;
                        break;
                }
                unitOfWork.UserRepositery.Update(usr);
                unitOfWork.Save();
            }
        }

        public List<WorkItemState> GetFilterTasksList()
        {
            var lst = new List<WorkItemState>();
            if (CurrentUser.FilterListToDo) lst.Add(WorkItemState.Todo);
            if (CurrentUser.FilterListInProgress) lst.Add(WorkItemState.InProgress);
            if (CurrentUser.FilterListDone) lst.Add(WorkItemState.Done);
            if (CurrentUser.FilterListReopen) lst.Add(WorkItemState.Reopen);
            return lst;
        }

        public List<WorkItemType> GetFilterWorkItemType()
        {
            var lst = new List<WorkItemType>();
            if (CurrentUser.FilterListTask) lst.Add(WorkItemType.Task);
            if (CurrentUser.FilterListBug) lst.Add(WorkItemType.Defect);
            return lst;
        }

        public List<String> GetFilterWorkItemTypeString()
        {
            var lst = new List<String>();
            if (CurrentUser.FilterListTask) lst.AddRange(new[] { "Task", "Tâche" });
            if (CurrentUser.FilterListBug) lst.AddRange(new[] { "Bug", "Bogue" });
            return lst;
        }

        public List<Boolean> GetFilterCheckBoxList(int userId)
        {
            this.CurrentUserID = userId;
            var lst = new List<Boolean>
                          {
                               this.CurrentUser.FilterListToDo,
                               this.CurrentUser.FilterListInProgress,
                               this.CurrentUser.FilterListDone,
                               this.CurrentUser.FilterListReopen,
                               this.CurrentUser.FilterListUnestimated,
                               this.CurrentUser.FilterListTask,
                               this.CurrentUser.FilterListBug
                          };
            return lst;
        }

        public TaskForScore GetTaskScore(Int32 taskId)
        {
            var wie = TFS.GetWorkItem(taskId, false);
            var t = new TaskForScore
            {
                ID = taskId,
                Level = wie.GetLevel(),
                Duration = wie.GetDuration(),
                Priority = wie.GetPrecedence(),
                Date = wie.GetScoringDate() ?? DateTime.UtcNow
            };

            return t;
        }

        public Boolean SetTaskScore(TaskForScore score)
        {
            var wie = TFS.GetWorkItem(score.ID, true);
            wie.SetLevel(score.Level);
            wie.SetDuration(score.Duration);
            wie.SetPriority(score.Priority);
            wie.SetScoringDate(score.Date);
            return TFS.SaveWorkItem(wie);
        }

        public TaskForMove GetTaskMove(Int32 taskId)
        {
            var wie = TFS.GetWorkItem(taskId, false);
            var t = new TaskForMove
            {
                ID = taskId,
                MoveOrCopy = false,
                CurrentProject = wie.Project.Name,
                NewProjectId = -1,
                NewProjects = TFS.GetDictProjects()
            };
            return t;
        }

        public Boolean SetTaskMove(TaskForMove taskMove, TFSServices tfs)
        {
            if (taskMove.NewProjectId >= 0)
            {
                var wie = tfs.GetWorkItem(taskMove.ID, false);
                var prj = tfs.GetProject(taskMove.NewProjectId);
                if (prj.WorkItemTypes.Contains(wie.Type.Name))
                {
                    var destWit = prj.WorkItemTypes[wie.Type.Name];

                    var wim = wie.Copy(destWit);
                    var aList = wie.Validate();
                    if (aList.Count == 0)
                    {
                        if (tfs.SaveWorkItem(wim) && !taskMove.MoveOrCopy)
                        {
                            var ids = new List<int> { wie.Id };
                            // ReSharper disable IteratorMethodResultIsIgnored
                            tfs.TfsWorkItemStore.DestroyWorkItems(ids);
                            // ReSharper restore IteratorMethodResultIsIgnored
                            tfs.TfsWorkItemStore.RefreshCache();
                            tfs.TfsWorkItemStore.SyncToCache();
                        }
                    }
                }

            }
            return false;
        }

        private String CutProjectName(String projectName, String stringToCut)
        {
            var sRet = stringToCut;
            if (sRet.StartsWith(projectName) && sRet.Length > projectName.Length)
                sRet = sRet.Substring(projectName.Length + 1);
            return sRet;
        }
    }
}
